package com.fire.market.order.manager;


import com.fire.component.cache.local.RedisOperate;
import com.fire.component.extension.Assert;
import com.fire.component.extension.BizCommonError;
import com.fire.component.extension.ExceptionFactory;
import com.fire.market.gateway.api.dto.UserInfo;
import com.fire.market.order.ProductIntegrationService;
import com.fire.market.order.UserIntegrationService;
import com.fire.market.order.api.request.OrderCreateParam;
import com.fire.market.order.common.interceptor.IdGeneratorSnowflake;
import com.fire.market.order.entity.OrderInfoDo;
import com.fire.market.order.model.OrderModel;
import com.fire.market.order.repository.OrderRepository;
import com.fire.market.order.tcc.ProductSalesIncreaseTcc;
import com.fire.market.product.api.dto.ItemInfo;
import org.apache.commons.lang3.StringUtils;
import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormat;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

@Component
public class OrderManager {
    @Resource
    private ProductIntegrationService productIntegrationService;
    @Resource
    private UserIntegrationService userIntegrationService;
    @Resource
    private OrderRepository orderRepository;
    @Resource
    private IdGeneratorSnowflake idGeneratorSnowflake;

    @Resource
    private ProductSalesIncreaseTcc productSalesIncreaseTcc;
    /**
     * 风控模型中引入对缓存进行，使用数据库效率低
     *
     * @param itemId
     * @param userId
     * @param amount
     * @param promoId
     * @return
     */
    public ItemInfo verifyOrderStatus(Integer itemId, Integer userId, Integer amount, Integer promoId) {
        //商品校验缓存
        ItemInfo cacheItemInfo = RedisOperate.get("item_validate_" + itemId, ItemInfo.class);
        ItemInfo itemDetail = null;
        if (cacheItemInfo == null) {
            itemDetail = productIntegrationService.getItemDetail(itemId);
            if (itemDetail == null) {
                throw ExceptionFactory.bizException(BizCommonError.PARAMETER_VALIDATION_ERROR.getErrCode(),"商品信息不存在");
            }
            RedisOperate.set("item_validate_" + itemId, itemDetail, 10, TimeUnit.MINUTES);
        } else {
            itemDetail = cacheItemInfo;
        }

        //用户校验缓存
        UserInfo cacheUserInfo = RedisOperate.get("user_validate_" + userId, UserInfo.class);
        UserInfo userInfo = null;
        if (cacheUserInfo == null) {
            userInfo = userIntegrationService.queryUserInfo(userId);
            if (userInfo == null) {
                throw ExceptionFactory.bizException(BizCommonError.USER_NOT_EXIST.getErrCode(),
                        BizCommonError.USER_NOT_EXIST.getErrMsg());
            }
            RedisOperate.set("user_validate_" + userId, userInfo,10, TimeUnit.MINUTES);
        }

        if (amount <= 0 || amount > 99) {
            throw ExceptionFactory.bizException(BizCommonError.PARAMETER_VALIDATION_ERROR.getErrCode(), "商品数量信息不存在");
        }

        //校验活动信息
        if (promoId != null) {
            //校验对应活动是否存在这个适用商品
            if (promoId.intValue() != itemDetail.getPromoId()) {
                throw ExceptionFactory.bizException(BizCommonError.PARAMETER_VALIDATION_ERROR.getErrCode(), "活动信息不正确");
            } else if (itemDetail.getStatus() != 2) { // 校验活动是否正在进行中
                throw ExceptionFactory.bizException(BizCommonError.PARAMETER_VALIDATION_ERROR.getErrCode(), "活动尚未开始");
            }
        }
        return itemDetail;
    }


    public void decreaseStock(Integer itemId, Integer amount) {
        Assert.notNull(itemId, "itemId is null");
        Assert.notNull(amount, "amount is null");
        boolean result = productIntegrationService.decreaseItemStockByCache(itemId, amount);
        if (!result) {
            throw ExceptionFactory.bizException(BizCommonError.STOCK_NOT_ENOUGH.getErrCode(),
                    BizCommonError.STOCK_NOT_ENOUGH.getErrMsg());
        }
    }

    public void increaseStock(Integer itemId, Integer amount) {
        Assert.notNull(itemId, "itemId is null");
        Assert.notNull(amount, "amount is null");
        productIntegrationService.increaseItemStockByCache(itemId, amount);
    }

    public void saveOrder(OrderModel orderModel) {
        Assert.notNull(orderModel, "orderModel is null");
        OrderInfoDo orderInfoDo = convertFromOrderModel(orderModel);
        orderRepository.insertOrder(orderInfoDo);
    }

    private OrderInfoDo convertFromOrderModel(OrderModel orderModel) {
        if (orderModel == null) {
            return null;
        }
        OrderInfoDo orderInfoDo = new OrderInfoDo();
        BeanUtils.copyProperties(orderModel, orderInfoDo);
        orderInfoDo.setOrderPrice(orderModel.getOrderPrice().doubleValue());
        return orderInfoDo;
    }

    /**
     * 此处异步扣件数据库的解决思路是，不回滚。会将错误信息进行记录。等待后续人工排查
     *
     * @param param
     * @param itemInfo
     * @return
     */
    @Transactional(rollbackFor = Throwable.class)
    public OrderModel createOrderInTransaction(OrderCreateParam param, ItemInfo itemInfo) {
        //创建订单ID
        String sequenceId = String.valueOf(idGeneratorSnowflake.snowflakeId());
        //订单入库
        OrderModel orderModel = new OrderModel();
        orderModel.setId(sequenceId);
        if (param.getPromoId() != null && itemInfo.getStatus() != 0) {//更具秒杀状态选择不同金额
            orderModel.setItemPrice(itemInfo.getPromoItemPrice());
            orderModel.setOrderPrice(itemInfo.getPromoItemPrice().multiply(BigDecimal.valueOf(param.getAmount())));
        } else {
            orderModel.setItemPrice(itemInfo.getPrice());
            orderModel.setOrderPrice(itemInfo.getPrice().multiply(BigDecimal.valueOf(param.getAmount())));
        }
        orderModel.setItemId(itemInfo.getId());
        orderModel.setAmount(param.getAmount());
        orderModel.setUserId(param.getUserId());
        orderModel.setPromoId(itemInfo.getPromoId());
        this.saveOrder(orderModel);

        //商品销量增加
//        productIntegrationService.increaseSales(param.getItemId(), param.getAmount());
        productSalesIncreaseTcc.prepare(param);
        //异步扣件数据库
        productIntegrationService.syncDecreaseItemStockByMq(param.getItemId(), param.getAmount(), sequenceId, param.getUserId());
        return orderModel;
    }

    public String queryPromoToken(Integer promoId, Integer amount, Integer itemId, Integer userId) {
        //校验token
        String redisToken = RedisOperate.get("promo_item_" + itemId + "_userId_" + userId, String.class);
        if (!StringUtils.isEmpty(redisToken)) {
            return redisToken;
        }
        ItemInfo itemInfo = this.verifyOrderStatus(itemId, userId, amount, promoId);
        //获取秒杀大闸的count数量
        long result = RedisOperate.incr("promo_door_count_" + promoId, -1);
        if (result < 0) {
            return null;
        }
        //2021-06-0213:04:30
        org.joda.time.format.DateTimeFormatter dateTimeFormatter = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss");
        StringBuilder startDate = new StringBuilder(itemInfo.getStartDate());
        startDate.insert(10, " ");
        DateTime dateTimeStart = dateTimeFormatter.parseDateTime(startDate.toString());
        StringBuilder endDate = new StringBuilder(itemInfo.getEndDate());
        endDate.insert(10, " ");
        DateTime dateTimeEnd = dateTimeFormatter.parseDateTime(endDate.toString());
        long millis = dateTimeEnd.getMillis() - dateTimeStart.getMillis();
        String token = UUID.randomUUID().toString().replace("-", "");
        RedisOperate.set("promo_item_" + itemId + "_userId_" + userId, token, millis, TimeUnit.MILLISECONDS);
        return token;
    }

    public void verify(OrderCreateParam param) {
        Assert.notNull(param.getPromoToken(), "promoToken is null");
        Assert.notNull(param.getItemId(),   "itemId is null");
        Assert.notNull(param.getUserId(), "userId is null");
        String token = RedisOperate.get("promo_item_" + param.getItemId() + "_userId_" + param.getUserId(), String.class);
        if (StringUtils.isEmpty(token) || token.equals(param.getPromoToken())){
            throw ExceptionFactory.bizException(BizCommonError.PARAMETER_VALIDATION_ERROR.getErrCode(),
                    BizCommonError.PARAMETER_VALIDATION_ERROR.getErrMsg());
        }
    }

}
